package org.hilo.boot.core.web;

import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.WebUtils;

import org.hilo.boot.core.UT;

/**
 * 
 * @author zollty
 * @since 2020年11月18日
 */
public class HttpServletRequestHelper {
    private HttpServletRequest request;
    private static final Logger logger = LoggerFactory.getLogger(HttpServletRequestHelper.class);
    
    private HttpServletRequestHelper(HttpServletRequest request) {
        this.request = request;
    }
    
    public static HttpServletRequestHelper build(HttpServletRequest request) {
        return new HttpServletRequestHelper(request);
    }
    
    public Map<String, String[]> getParameterMap() {
        return request.getParameterMap();
    }
    
    public String getRequestIp() {
        return UT.Web.getIpAddr(request);
    }
    
    public Map<String, String> getHeders() {
        return getHeders(request);
    }
    
    public static Map<String, String> getHeders(HttpServletRequest request) {
        Map<String, String> map = new HashMap<>();
        Enumeration<String> headerNames = request.getHeaderNames();
        String key;
        while (headerNames.hasMoreElements()) {
            key = headerNames.nextElement();
            map.put(key, request.getHeader(key));
        }
        return map;
    }
    
    public Map<String, Object> getSessionData() {
        return getSessionData(request.getSession(false));
    }
    
    public String getBodyIfNecessary() {
        return getBodyIfNecessary(request);
    }
    
    public String getRequestInfo(boolean readBody, boolean readSession) {
        return getRequestInfo(request, readBody, readSession);
    }
    
    @SuppressWarnings("unchecked")
    public static Map<String, Object> getSessionData(HttpSession session) {
        if (session == null) {
            return Collections.EMPTY_MAP;
        }
        StringBuilder info = new StringBuilder();
        info.append("Id_").append(session.getId());
        info.append(", ").append("CreationTime_")
                .append(UT.Date.format_yyyy_MM_dd_HH_mm_ss(new Date(session.getCreationTime())));
        Map<String, Object> map = new HashMap<>();
        map.put("sessionInfo", info.toString());
        // 获取session中所有的键值
        Enumeration<String> enumeration = session.getAttributeNames();
        // 遍历enumeration中的
        while (enumeration.hasMoreElements()) {
            // 获取session键值
            String name = enumeration.nextElement();
            // 根据键值取session中的值
            Object value = session.getAttribute(name);
            map.put(name, value);
        }
        return map;
    }
    
    public static String getRequestInfo(HttpServletRequest request, boolean readBody, boolean readSession) {
        return getRequestInfoBuilder(request, readBody, readSession).toString();
    }
    
    /**
     * @return 所有的请求参数、完整的请求信息
     */
    public static StringBuilder getRequestInfoBuilder(HttpServletRequest request, boolean readBody, 
            boolean readSession) {
        StringBuilder sb = new StringBuilder(1024);
        sb.append("-----------------------").append(UT.Date.format_yyyy_MM_dd_HH_mm_ss(new Date()))
                .append("-------------------------------------\n");
        sb.append("URL        : ").append(request.getMethod()).append(" ").append(request.getRequestURL()).append("\n");
        // 请求方的 ip地址
        sb.append("RequestIP  : ").append(UT.Web.getIpAddr(request)).append("\n");
        sb.append("Queries    : ").append(request.getQueryString()).append("\n");
        sb.append("Parameter  : ").append(UT.Json.toJSONString(request.getParameterMap())).append("\n");
        if (readBody) {
            sb.append("Body       : ").append(getBodyIfNecessary(request)).append("\n");
        }
        sb.append("Headers    : ").append(UT.Json.toJSONString(getHeders(request))).append("\n");
        if (readSession) {
            sb.append("Session    : ").append(UT.Json.toJSONString(getSessionData(request.getSession(false))))
                    .append("\n");
        }
        sb.append("Special    : ");
        sb.append("(1 ").append(request.getCharacterEncoding()).append(") ");
        sb.append("(2 ").append(request.getContentLength()).append(") ");
        sb.append("(3 ").append(request.getContentType()).append(") ");
        sb.append("(4 ").append(request.getDispatcherType()).append(") ");
        sb.append("(5 ").append(request.getLocale()).append(") ");
        sb.append("(6 ").append(request.getUserPrincipal()).append(") \n");
        return sb;
    }
    
    public static String getBodyIfNecessary(HttpServletRequest request) {
        HiloHttpServletRequestWrapper wrapper = WebUtils.getNativeRequest(request, HiloHttpServletRequestWrapper.class);
        if (wrapper != null && RequestBodyParser.shouldParseBody(wrapper)) {
            try {
                return wrapper.getBodyString();
            } catch (IOException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("can not parse body to string, uri=" + request.getRequestURI(), e);
                }
            }
        }
        return RequestBodyParser.BODY_NO_PARSE_FLAG;
    }
    
    public static boolean shouldGetBody(HttpServletRequest request) {
        HiloHttpServletRequestWrapper wrapper = WebUtils.getNativeRequest(request, HiloHttpServletRequestWrapper.class);
        if (wrapper != null && RequestBodyParser.shouldParseBody(wrapper)) {
            return true;
        }
        return false;
    }
    
    public static String getBody(HiloHttpServletRequestWrapper wrapper) {
        try {
            return wrapper.getBodyString();
        } catch (IOException e) {
            if (logger.isDebugEnabled()) {
                logger.debug("can not parse body to string, uri=" + wrapper.getRequestURI(), e);
            }
            return RequestBodyParser.BODY_NO_PARSE_FLAG;
        }
    }

}
